package app

import (
	"bytes"
	"context"
	"os"
	"os/exec"
	"os/signal"
	"runtime/debug"
	"sieve_admin_server_userpass/lib/log"
	"strconv"
	"sync"
	"syscall"
	"time"
)

var (
	// 程序内所有新context都必须以CTX为parent
	CTX, cancel = context.WithCancel(context.Background())
	wg          = &sync.WaitGroup{}
)

func init() {
	chCloseSig := make(chan os.Signal)
	signal.Notify(chCloseSig, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		sig := <-chCloseSig
		log.Info("recv SIG", sig)
		SafeExit()
	}()
	//
	if os.Getenv("__MP_CHILD_ID__") != "" {
		go func() {
			for {
				if os.Getppid() == 1 {
					SafeExit()
				}
				time.Sleep(time.Second)
			}
		}()
	}
}

// 强制退出超时时间，默认5秒
var ForceExitTimeout = time.Second * 5

func SafeExit() {
	cancel()
	if ForceExitTimeout > 0 {
		go func() {
			time.Sleep(ForceExitTimeout)
			log.Fatal("SAFE_EXIT: some of the goroutines not safely exit in", time.Duration(ForceExitTimeout).String())
		}()
	}
	wg.Wait()
	os.Exit(0)
}

func Async(fn func()) {
	go func() {
		defer log.RecoverPanic()
		fn()
	}()
}

// 确保异步执行完之前进程不会退出
// 必须响应全局app.CTX，否则wg死锁至强制退出
func AsyncSafe(fn func()) {
	wg.Add(1)
	go func() {
		defer log.RecoverPanic()
		defer wg.Done()
		fn()
	}()
}

// 确保函数执行完之前进程不会退出
// 必须响应全局app.CTX，否则wg死锁至强制退出
func CallSafe(fn func()) {
	defer log.RecoverPanic()
	wg.Add(1)
	defer wg.Done()
	fn()
}

func Block() {
	<-make(chan struct{})
}

func SetOpenFiles(limit int) {
	if limit > 0 {
		Async(func() {
			exec.Command("prlimit", "--pid="+strconv.Itoa(os.Getpid()), "--nofile="+strconv.Itoa(limit)).Run()
		})
	}
}

var gBegin = []byte("goroutine ")

func GoroutineID() uint64 {
	b, ok := bytes.CutPrefix(debug.Stack(), gBegin)
	if !ok {
		return 0
	}
	i := bytes.IndexByte(b, ' ')
	if i < 0 {
		return 0
	}
	b = b[:i]
	n, _ := strconv.ParseUint(string(b), 10, 64)
	return n
}
